<?php

namespace libs;

class Dispatcher {

    protected static $_instance = null;
    protected $_controllersPath = null;

    protected function __construct($controllersPath) {
        $this->setControllersPath($controllersPath);
    }

    public static function getInstance($controllersPath = PATH_CONTROLLERS) {
        if (self::$_instance === null)
            self::$_instance = new self($controllersPath);

        return self::$_instance;
    }

    public function setControllersPath($path) {
        if (!is_dir($path))
            throw new \Exception('Directory "' . $path . '" do not exists');
        if (!is_writable($path))
            throw new \Exception('Directory "' . $path . '" is not writable');
        $this->_controllersPath = realpath($path) . DIRECTORY_SEPARATOR;
    }

    public function run() {
        // On analyse l'url
        $controller = isset($_GET['controller']) ? $_GET['controller'] : 'Index';
        $action = isset($_GET['action']) ? $_GET['action'] : false;

        // On regarde si la classe exists, si oui on tente de demarrer le controller, sinon 404
        if (file_exists($this->_controllersPath . ucfirst($controller) . '.class.php'))
            $this->_runController('controllers\\' . ucfirst($controller), $action);
        else
            $this->show404();
    }

    public function show404() {
        if (!headers_sent()) {
            header('HTTP/1.0 404 Not Found');
            header('Status: 404 Not Found');
        }
        echo 'PAGE 404';
        // On tue le script
        exit();
    }

    protected function _runController($controller, $action) {
        // On valid le controller ;)
        $this->_checkController($controller, $action);
        // On instancie notre controller, et on lance l'action demandéé ( si y'en a une bien sur...)
        $ctrl = new $controller();
        if ($action)
            $ctrl->$action();

        // On affiche le template qui aura été définit dans le controller, si aucun template n'est définit, une valeur par defaut sera appélée
        $ctrl->display();
    }

    protected function _checkController($controller, $action) {
        // On construit une classe "nulle" avec comme paramètre notre controller, comme ça on va pouvoir le verifier
        $inst = new \ReflectionClass($controller);
        // Notre controller ne peut pas être une interface, ni une classe abstraite (non instanciable) logique ...
        if ($inst->isInterface() || $inst->isAbstract())
            throw new \Exception('Controller "' . $controller . '" cannot be an interface of an abstract class');
        // Il doit implementer l'interface IController, comme ça on est sur d'avoir les methodes obligatoire, définis ...
        if (!in_array('libs\\mvc\\IController', $inst->getInterfaceNames()))
            throw new \Exception('Controller "' . $controller . '" must be implement libs\mvc\IController');

        // On verifie qu'il est la methode display (normalment l'interface l'aura demandé au préalable ;) Cette methode sert à afficher le template
        if (!$inst->hasMethod('display'))
            throw new \Exception('Controller "' . $controller . '" must be implement method "Display"');

        // Si une action est demandée, on verifie que celle-ci exists ... si elle existe pas, on balance la page 404 ;)
        if ($action) {
            if (!$inst->hasMethod($action))
                $this->show404();
        }
    }

}

?>
